﻿/*
Copyright © 2011 François Karman

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

See <license.txt> for the complete license of the software.
*/

using System;
using System.Collections.Generic;

namespace Simple.Storage
{
    /// <summary>
    /// The default implementation of the <see cref="IDataSourceDefinition"/>
    /// interface.
    /// </summary>
    public class DefaultDataSourceDefinition : IDataSourceDefinition
    {
        /// <summary>
        /// The name of the data source.
        /// </summary>
        private readonly string name;

        /// <summary>
        /// The definitions of the classes that compose the data source.
        /// </summary>
        private readonly ICollection<IClassDefinition> classes;

        /// <summary>
        /// The read-only collection associated with
        /// <see cref="DefaultDataSourceDefinition.classes"/>.
        /// </summary>
        private readonly ICollection<IClassDefinition> classesRO;

        /// <summary>
        /// The definitions of the indexes associated to the data source.
        /// </summary>
        private readonly ICollection<IIndexDefinition> indexes;

        /// <summary>
        /// The read-only collection associated with
        /// <see cref="DefaultDataSourceDefinition.indexes"/>.
        /// </summary>
        private readonly ICollection<IIndexDefinition> indexesRO;

        /// <summary>
        /// Initializes a new instance of the DefaultDataSourceDefinition class.
        /// </summary>
        /// <param name="name">The name of the data source.</param>
        public DefaultDataSourceDefinition(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            this.classes = new LinkedList<IClassDefinition>();
            this.classesRO = new ReadOnlyCollection<IClassDefinition>(this.classes);
            this.indexes = new LinkedList<IIndexDefinition>();
            this.indexesRO = new ReadOnlyCollection<IIndexDefinition>(this.indexes);
            this.name = name;
        }

        /// <summary>
        /// Gets the name of the data source.
        /// </summary>
        /// <remarks>
        /// Should be unique in the encapsulating application to avoid collision during
        /// save and load operations.
        /// </remarks>
        public string Name
        {
            get { return this.name; }
        }

        /// <summary>
        /// Gets the definitions of the classes that compose the data source.
        /// </summary>
        /// <remarks>
        /// This collection is read-only.
        /// </remarks>
        public ICollection<IClassDefinition> Classes
        {
            get { return this.classesRO; }
        }

        /// <summary>
        /// Gets the definitions of the indexes associated to the data source.
        /// </summary>
        /// <remarks>
        /// This collection is read-only.
        /// </remarks>
        public ICollection<IIndexDefinition> Indexes
        {
            get { return this.indexesRO; }
        }

        /// <summary>
        /// Creates and adds a class definition to the data source
        /// based on a class with a default constructor.
        /// </summary>
        /// <typeparam name="T">
        /// The type of the persisted objects.
        /// </typeparam>
        /// <typeparam name="TKey">
        /// The type of the identifier associated with <typeparamref name="T"/>.
        /// </typeparam>
        /// <param name="retrieveKey">
        /// The function used to extract the identifier of a <typeparamref name="T"/> object.
        /// </param>
        /// <returns>
        /// The current data source definition to authorize chained calls.
        /// </returns>
        public DefaultDataSourceDefinition AddClass<T, TKey>(Func<T, TKey> retrieveKey)
            where T : new()
        {
            this.classes.Add(new Api.DefaultClassDefinition<T, TKey>(retrieveKey, i => new T()));
            return this;
        }

        /// <summary>
        /// Creates and adds a class definition to the data source
        /// by providing how to initialize a new instance.
        /// </summary>
        /// <typeparam name="T">
        /// The type of the persisted objects.
        /// </typeparam>
        /// <typeparam name="TKey">
        /// The type of the identifier associated with <typeparamref name="T"/>.
        /// </typeparam>
        /// <param name="retrieveKey">
        /// The function used to extract the identifier of a <typeparamref name="T"/> object.
        /// </param>
        /// <param name="createInstance">
        /// The function used to create new instance of the <typeparamref name="T"/> class.
        /// </param>
        /// <returns>
        /// The current data source definition to authorize chained calls.
        /// </returns>
        public DefaultDataSourceDefinition AddClass<T, TKey>(Func<T, TKey> retrieveKey, Func<TKey, T> createInstance)
        {
            this.classes.Add(new Api.DefaultClassDefinition<T, TKey>(retrieveKey, createInstance));
            return this;
        }

        /// <summary>
        /// Creates and adds an index definition to the data source.
        /// </summary>
        /// <typeparam name="T">
        /// The type of the persisted objects.
        /// </typeparam>
        /// <typeparam name="TIndex">
        /// The type of theindex values.
        /// </typeparam>
        /// <param name="name">
        /// The name of the index.
        /// </param>
        /// <param name="retrieveIndex">
        /// The function used to extract the index value from an instance
        /// of the <typeparamref name="T"/> class.
        /// </param>
        /// <returns>
        /// The current data source definition to authorize chained calls.
        /// </returns>
        public DefaultDataSourceDefinition AddIndex<T, TIndex>(string name, Func<T, TIndex> retrieveIndex)
        {
            this.indexes.Add(new Api.DefaultIndexDefinition<T, TIndex>(name, retrieveIndex));
            return this;
        }
    }
}
